package com.markyao.service;

import com.markyao.service.redis.UtilsKeys;
import com.markyao.vo.PageParams;
import com.markyao.vo.RegistryBean;
import com.markyao.vo.pojo.AppBean;
import com.markyao.vo.pojo.ServiceBean;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Slf4j(topic = "m.AbstractViewService")
public abstract class AbstractViewService implements ViewService{

    protected final List<AppBean> appbeanBuffers =new ArrayList<>(256);
    protected final List<ServiceBean> servicesBuffers =new ArrayList<>(256);
    protected final ExecutorService executors= Executors.newSingleThreadExecutor(new CustomizableThreadFactory("REFRESH-BUFFER-WORKER"));
    
    
    protected abstract void  refreshAppbeansBuf(List<AppBean> appbeanBuffers);
    protected abstract void  refreshServicesBuffers(List<ServiceBean> servicesBuffers);
    /**
     * 定时刷新缓存.
     */
    @PostConstruct
    private void refreshBuffers(){
        executors.submit(()->{
            while (true){
                appbeanBuffers.clear();
                refreshAppbeansBuf(appbeanBuffers);
                servicesBuffers.clear();
                refreshServicesBuffers(servicesBuffers);
                Thread.sleep(3 * 1000);
            }
        });
    }

    @Override
    public List<AppBean> getList(PageParams pageParams) {
        Integer currentPage = pageParams.getCurrentPage();
        Integer pageSize = pageParams.getPageSize();
        if (null == appbeanBuffers || appbeanBuffers.size() ==0 || null == currentPage || currentPage<=0) {
            return Collections.emptyList();
        }
        pageParams.setTotal((long) appbeanBuffers.size());
        int toIndex = currentPage + pageSize -1;
        if (toIndex>= appbeanBuffers.size()) {
            toIndex= appbeanBuffers.size();
        }
        return appbeanBuffers.subList(currentPage - 1, toIndex);
    }

    @Override
    public List<ServiceBean> getServices(PageParams pageParams) {
        Integer currentPage = pageParams.getCurrentPage();
        Integer pageSize = pageParams.getPageSize();
        if (null == servicesBuffers || servicesBuffers.size() ==0 || null == currentPage || currentPage<=0) {
            return Collections.emptyList();
        }
        pageParams.setTotal((long) servicesBuffers.size());
        int toIndex = currentPage + pageSize -1;
        if (toIndex>= servicesBuffers.size()) {
            toIndex= servicesBuffers.size();
        }
        return servicesBuffers.subList(currentPage - 1, toIndex);
    }

    protected String buildAlive(String appName,String url){
        Boolean hasKey = isAlive(appName,url);
        String alive=hasKey ? aliveValue : deadValue;
        return alive;
    }


    private Boolean isAlive(String appName, String url) {
        Boolean f = isHeartBeat(appName);
        if (f){
            return f;
        }
        String heartBeatKey = UtilsKeys.buildHeartBeatKey(appName, url);
        return isHeartBeat(heartBeatKey);
    }
    private boolean isAlive(String appName) {
        String url=getUrl(appName);
        return isAlive(appName,url);
    }

    //        String key = UtilsKeys.SERVER_REGISTRY_KEY + appName;
    protected abstract String getUrl(String appName);
    protected abstract Boolean isHeartBeat(String heartBeatKey);

    @Override
    public RegistryBean getRegistryBean(String appName) {
        Map<Object, Object> basicMp = getBeanMapFromServersByAppName(appName);
////redisTemplate.opsForHash().entries(UtilsKeys.SERVER_REGISTRY_KEY + appName);
        RegistryBean registryBean=new RegistryBean();

        Object o1 = basicMp.get(UtilsKeys.APPNAME);
        Object o2 = basicMp.get(UtilsKeys.URL);
        Object o3 = basicMp.get(UtilsKeys.WEIGHT);
        AppBean appBean=new AppBean();
        appBean.setAppName(o1.toString());
        appBean.setUrl(o2.toString());
        appBean.setWeight(o3.toString());
        appBean.setAlive(getHealth(appName));

        registryBean.setAppBean(appBean);

        Boolean isProvider = isProvider(appName);
        //redisTemplate.hasKey(UtilsKeys.SERVER_REGISTRY_KEY + appName)
        if (!isProvider) {
            registryBean.setRegistedServices(Collections.emptyList());
        } else {
            List<String> registedServices = new ArrayList<>(16);
            Map<Object, Object> entries = getBeanMapFromServicesByAppName(appName);
    //redisTemplate.opsForHash().entries(UtilsKeys.SERVICE_REGISTRY_KEY + appName);
            for (Object k : entries.keySet()) {
                String m=k.toString()+":: 实现 ==> "+entries.get(k).toString();
                registedServices.add(m);
            }
            registryBean.setRegistedServices(registedServices);
        }

        Boolean isRefrence = isRefrence(appName);
        //redisTemplate.hasKey(UtilsKeys.REFRENCE_REGISTRY_KEY + appName)
        if (!isRefrence){
            registryBean.setNeedingsServices(Collections.emptyList());
        }else {
            Set<String> refrences = getRefrenceSetByAppName(appName);
            //redisTemplate.opsForSet().members(UtilsKeys.REFRENCE_REGISTRY_KEY + appName)
            registryBean.setNeedingsServices(new ArrayList<>(refrences));
        }

        Boolean isConnecting = isConnecting(appName);
        //redisTemplate.hasKey(UtilsKeys.CONNECT_KEY + appName)
        if (isConnecting){
            Set<String> members = getConnectSetByAppName(appName);
                    //redisTemplate.opsForSet().members(UtilsKeys.CONNECT_KEY + appName);
            ArrayList connectings = new ArrayList<>(members.size());
            for (String member : members) {
                if (isAlive(member)){
                    connectings.add(member);
                }
            }
            registryBean.setConnectings(connectings);
        }else {
            Set<String>connetings=new HashSet<>();
            Set<String> keys = getConnectSetByKey();
                    //redisTemplate.keys(UtilsKeys.CONNECT_KEY + "*");
            if (keys.size()>0){
                for (String key : keys) {
                    Set<String> members = getConnectSetByKey(key);
                    //redisTemplate.opsForSet().members(key)
                    for (String member : members) {
                        if (member.equals(appName)){
                            connetings.add(key);
//                            connetings.add(key.substring(UtilsKeys.CONNECT_KEY.length()));
                            break;
                        }
                    }
                }
            }
            if (connetings.size()>0){
                registryBean.setConnectings(new ArrayList<>(connetings));
            }
        }

        return registryBean;
    }
    protected abstract String getHealth(String appName);


    protected abstract Set<String> getConnectSetByAppName(String appName);

    protected abstract Set<String> getConnectSetByKey();

    protected abstract Set<String> getConnectSetByKey(String appName);

    protected abstract Boolean isConnecting(String appName);

    protected abstract Set<String> getRefrenceSetByAppName(String appName);

    protected abstract Boolean isRefrence(String appName);

    protected abstract Map<Object, Object> getBeanMapFromServicesByAppName(String appName);

    protected abstract Map<Object, Object> getBeanMapFromServersByAppName(String appName);

    protected abstract Boolean isProvider(String appName);


    @Override
    public abstract void closeConnect(String curApp, String tarApp);

    @Override
    public abstract void reConnect(String curApp, String tarApp);
}
